每天的專案會同步到 GitLab 上,可以前往 GitLab 查看,有興趣的朋友歡迎留言或來信討論,我的信箱是 nickchen1998@gmail.com。
本次爬文的目標是 衛福部 台灣 e 院。
警語:本篇所介紹的網路爬蟲技術僅供教學與研究用途,請勿將其用於攻擊行為或商業利用,違反相關法律責任自負。
在網路爬蟲的世界中,靜態網頁的資料擷取相對簡單,但當遇到需要 JavaScript 生成內容的動態網頁時,就需要使用像 Selenium 這樣的工具來模擬瀏覽器行為。今天,我們將深入探討如何使用 Selenium 來擷取動態網頁的資料,並逐步解析程式碼。
我們將以下列程式碼為例,逐段進行分析。
import re
import time
from pprint import pprint
from datetime import datetime
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
首先,我們引入所需的模組:
re
:用於正則表達式的匹配。time
:控制程式的暫停時間。pprint
:美化輸出字典資料。datetime
:處理日期和時間。selenium.webdriver
:控制瀏覽器的核心模組。browser = Chrome()
browser.get('https://sp1.hso.mohw.gov.tw/doctor/Often_question/type_detail.php?q_type=排便問題&UrlClass=肝膽腸胃科')
time.sleep(10)
browser = Chrome()
:初始化一個 Chrome 瀏覽器實例。browser.get(url)
:打開指定的 URL。time.sleep(10)
:等待 10 秒,確保網頁內容完全載入。category = "排便問題"
doctor_department = "肝膽腸胃科"
我們設定了兩個變數:
category
:問題的分類。doctor_department
:醫生所屬的科別。for paragraph in browser.find_elements(By.CSS_SELECTOR, "ul.QAunit"):
使用 CSS 選擇器 ul.QAunit
找到所有相關的問答單元,並開始迭代。
time.sleep(10)
subject = paragraph.find_element(By.CSS_SELECTOR, "li.subject").text
li.subject
,並取得其文字內容,存入 subject
。asker_info = paragraph.find_element(By.CSS_SELECTOR, "li.asker").text
match = re.search(r'/(男|女)/.*?,(\d{4}/\d{2}/\d{2})', asker_info)
gender = match.group(1)
question_time = datetime.strptime(match.group(2), '%Y/%m/%d')
li.asker
,取得提問者的資訊。r'/(男|女)/.*?,(\d{4}/\d{2}/\d{2})'
:匹配格式如「/男/XXX,2023/10/01」的字串。gender
:提取到的性別。question_time
:將提問時間轉換為 datetime
對象。question = paragraph.find_element(By.CSS_SELECTOR, "li.ask").text
li.ask
,取得問題的詳細內容。answer = paragraph.find_element(By.CSS_SELECTOR, "li.ans").text
li.ans
,取得回答的內容。doctor_info = paragraph.find_element(By.CSS_SELECTOR, "li.doctor").text
match = re.search(r'/([\u4e00-\u9fa5]+),\s*(\d{4}/\d{2}/\d{2})', doctor_info)
doctor_name = match.group(1)
answer_time = datetime.strptime(match.group(2), '%Y/%m/%d')
li.doctor
,取得醫生的資訊。r'/([\u4e00-\u9fa5]+),\s*(\d{4}/\d{2}/\d{2})'
:匹配格式如「/張醫師, 2023/10/02」的字串。doctor_name
:提取到的醫生姓名。answer_time
:將回答時間轉換為 datetime
對象。view_info = paragraph.find_element(By.CSS_SELECTOR, "li.count").text
match = re.search(r'(\d+)', view_info)
view_amount = int(match.group(1)) if match else 0
li.count
,取得瀏覽次數的資訊。data = dict(
category=category,
subject=subject,
question=question,
gender=gender,
question_time=question_time,
answer=answer,
doctor_name=doctor_name,
doctor_department=doctor_department,
answer_time=answer_time,
view_amount=view_amount
)
pprint(data)
break
data
中。pprint
美化輸出字典內容。break
:只處理第一個問答單元,退出迴圈。browser.quit()
執行程式後,我們會得到類似以下的輸出:
{'answer': '醫生的回答內容...',
'answer_time': datetime.datetime(2023, 10, 2, 0, 0),
'category': '排便問題',
'doctor_department': '肝膽腸胃科',
'doctor_name': '張醫師',
'gender': '男',
'question': '提問者的問題內容...',
'question_time': datetime.datetime(2023, 10, 1, 0, 0),
'subject': '問題的主題',
'view_amount': 123}
這表示我們成功地從動態網頁中提取了所需的資料。
import re
import time
from pprint import pprint
from datetime import datetime
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
browser = Chrome()
browser.get('https://sp1.hso.mohw.gov.tw/doctor/Often_question/type_detail.php?q_type=排便問題&UrlClass=肝膽腸胃科')
category = "排便問題"
doctor_department = "肝膽腸胃科"
for paragraph in browser.find_elements(By.CSS_SELECTOR, "ul.QAunit"):
time.sleep(10)
subject = paragraph.find_element(By.CSS_SELECTOR, "li.subject").text
asker_info = paragraph.find_element(By.CSS_SELECTOR, "li.asker").text
match = re.search(r'/([男女])/.*?,(\d{4}/\d{2}/\d{2})', asker_info)
gender = match.group(1)
question_time = datetime.strptime(match.group(2), '%Y/%m/%d')
question = paragraph.find_element(By.CSS_SELECTOR, "li.ask").text
answer = paragraph.find_element(By.CSS_SELECTOR, "li.ans").text
doctor_info = paragraph.find_element(By.CSS_SELECTOR, "li.doctor").text
match = re.search(r'/([\u4e00-\u9fa5]+),\s*(\d{4}/\d{2}/\d{2})', doctor_info)
doctor_name = match.group(1)
answer_time = datetime.strptime(match.group(2), '%Y/%m/%d')
view_info = paragraph.find_element(By.CSS_SELECTOR, "li.count").text
match = re.search(r'(\d+)', view_info)
view_amount = int(match.group(1)) if match else 0
data = dict(
category=category,
subject=subject,
question=question,
gender=gender,
question_time=question_time,
answer=answer,
doctor_name=doctor_name,
doctor_department=doctor_department,
answer_time=answer_time,
view_amount=view_amount
)
pprint(data)
try:
next_page_element = browser.find_element(By.LINK_TEXT, "下一頁")
next_page_element.click()
except NoSuchElementException:
break
browser.quit()
透過 Selenium,我們能夠模擬使用者在瀏覽器上的操作,進而取得動態生成的網頁內容。本篇文章示範了如何使用 Selenium 結合正則表達式,從網頁中擷取結構化的資料。
time.sleep()
可以避免過於頻繁的請求。今天我們介紹了如何使用 Selenium 進行動態網頁的資料擷取,明天我們將加入問題與回答的重構。